package com.lastproject.ejb.dao;

import java.util.ArrayList;
import java.util.List;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import annotations.PermissionAnnotation;

import com.lastproject.dto.BookDTO;
import com.lastproject.ejb.entities.Book;
import com.lastproject.ejb.interceptors.PermissionInterceptor;
import com.lastproject.ejb.util.BookConversion;

/**
 * Session Bean implementation class BookDAO Author: Calin
 */
@Stateless
public class BookDAO implements BookDAORemote {

	private static final String BOOK_NOT_AVAILABLE = "notAvailable";

	@PersistenceContext
	EntityManager entityManager;

	@EJB
	BookConversion conversion;

	/**
	 * Default constructor.
	 */
	public BookDAO() {
		// TODO Auto-generated constructor stub
	}

	@Interceptors(PermissionInterceptor.class)
	@PermissionAnnotation(canCRUDOnBooks = 1)
	@Override
	public boolean create(boolean hasPermission, BookDTO bookDTO) {
		if (hasPermission) {
			entityManager.persist(conversion.fromDTO(bookDTO));
			return true;
		}

		else {
			return false;
		}
	}

	@Override
	public BookDTO findById(Object id) {
		Book book = entityManager.find(Book.class, id);
		return conversion.fromEntity(book);
	}

	@Override
	public List<BookDTO> getAll() {
		List<Book> bookss = entityManager.createQuery("Select b from Book b",
				Book.class).getResultList();
		List<BookDTO> booksDTO = new ArrayList<BookDTO>();

		for (Book book : bookss) {
			BookDTO bDTO = new BookDTO();
			bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);

		}

		return booksDTO;
	}

	@Interceptors(PermissionInterceptor.class)
	@PermissionAnnotation(canCRUDOnBooks = 1)
	@Override
	public boolean edit(boolean hasPermission, BookDTO bookDTO) {
		if (hasPermission) {
			entityManager.merge(conversion.fromDTO(bookDTO));
			return true;
		} else {
			return false;
		}
	}

	@Interceptors(PermissionInterceptor.class)
	@PermissionAnnotation(canCRUDOnBooks = 1)
	@Override
	public boolean remove(boolean hasPermission, BookDTO bookDTO) {
		if (hasPermission) {
			Book book = entityManager.find(Book.class,
					conversion.fromDTO(bookDTO).getId());
			entityManager.remove(book);
			return true;
		} else {
			return false;
		}
	}

	@Interceptors(PermissionInterceptor.class)
	@PermissionAnnotation(canCRUDOnBooks = 1)
	@Override
	public boolean removeBook(boolean permission, int bookId) {
		if (permission) {
			Book book = entityManager.find(Book.class, bookId);
			entityManager.remove(book);
			return true;
		}
		return false;
	}

	/**
	 * returneaza lista cartilor la care userul nu e in lista de asteptare, si
	 * nu sunt imprumutate de el
	 */
	@Override
	public List<BookDTO> getBooksNotInWaitingList(int id) {
		List<Book> books = entityManager
				.createQuery(
						"Select b from Book b "
								+ "where b.status <> :status and (b not in "
								+ "(Select w.book from WaitingList w where w.user.id like :id) "
								+ " and b not in "
								+ "(SELECT b.book from Borrowed b WHERE b.returnDate is null and b.user.id = :id)"
								+ " or b in (Select brw.book from Borrowed brw where brw.user.id <> :id and brw.returnDate is not null))",
						Book.class).setParameter("status", BOOK_NOT_AVAILABLE)
				.setParameter("id", id).getResultList();
		// if the book(s) borrowed by the current user is(are) in the waiting
		// list of other user, then remove them
		if (!getWaitListItemsForOtherUsers(id).isEmpty()) {
			List<Book> borrowedBooks = getBorrowedBooks(id);
			books.removeAll(borrowedBooks);
		}
		List<BookDTO> booksDTO = new ArrayList<BookDTO>();

		for (Book book : books) {
			BookDTO bDTO = new BookDTO();
			bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);
		}

		return booksDTO;
	}

	@Override
	public List<BookDTO> getBooksContainingText(String text) {
		List<Book> books = entityManager
				.createQuery("Select b from Book b where b.name like :text",
						Book.class).setParameter("text", "%" + text + "%")
				.getResultList();
		List<BookDTO> booksDTO = new ArrayList<BookDTO>();
		for (Book book : books) {
			BookDTO bDTO = new BookDTO();
			bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);
		}
		return booksDTO;
	}

	@Override
	public List<BookDTO> getBooksContainingTextAndNotInWaitingList(int id,
			String text) {
		List<Book> books = entityManager
				.createQuery(
						"Select b from Book b where b.id not in (Select w.book.id from WaitingList w where w.user.id like :id) and b.name like :text "
								+ " and b not in (SELECT bk from Book bk where bk in "
								+ "(SELECT b.book from Borrowed b WHERE b.returnDate is null and "
								+ " b.user.id = :idUser))", Book.class)
				.setParameter("text", "%" + text + "%").setParameter("id", id)
				.setParameter("idUser", id).getResultList();
		List<BookDTO> booksDTO = new ArrayList<BookDTO>();

		for (Book book : books) {
			BookDTO bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);
		}

		return booksDTO;
	}

	@Override
	public List<BookDTO> getBooksBorrowedByUser(int idUser) {

		List<Book> books = getBorrowedBooks(idUser);
		List<BookDTO> booksDTO = new ArrayList<BookDTO>();
		for (Book book : books) {
			BookDTO bDTO = new BookDTO();
			bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);
		}
		return booksDTO;
	}

	private List<Book> getWaitListItemsForOtherUsers(int idUser) {
		return entityManager
				.createQuery(
						"Select w.book from WaitingList w where w.user.id <> :id",
						Book.class).setParameter("id", idUser).getResultList();
	}

	private List<Book> getBorrowedBooks(int idUser) {
		return entityManager
				.createQuery(
						"SELECT b.book from Borrowed b WHERE b.returnDate is null and b.user.id = :idUser",
						Book.class).setParameter("idUser", idUser)
				.getResultList();
	}

	/**
	 * returneaza lista cartilor la care userul e in lista de asteptare si nu
	 * sunt imprumutate de el.
	 */
	@Override
	public List<BookDTO> getBooksWithUserInWaitingList(int id) {

		// Select b from Book b where b.id not in (Select w.book.id from
		// WaitingList w where w.user.id like :id)
		List<Book> books = entityManager
				.createQuery(
						"Select b from Book b where b.id in (Select w.book.id from WaitingList w where w.user.id like :id) "
								+ " and b not in (SELECT bk from Book bk where bk in "
								+ "(SELECT b.book from Borrowed b WHERE b.returnDate is null and "
								+ " b.user.id = :idUser))", Book.class)
				.setParameter("idUser", id).setParameter("id", id)
				.getResultList();

		List<BookDTO> booksDTO = new ArrayList<BookDTO>();

		for (Book book : books) {
			BookDTO bDTO = new BookDTO();
			bDTO = conversion.fromEntity(book);
			booksDTO.add(bDTO);
		}

		return booksDTO;
	}
}
